/*  2023/03/22 by DPH
*   建网过程中使用的勤务帧的头部
*   使用 自举中心勤务帧 来建网
*
*
*/

#include "serve-frame.h"
#include "ns3/rng-seed-manager.h"
#include "ns3/random-variable-stream.h"
#include "ns3/packet.h"
#include "ns3/ipv4-packet-info-tag.h"
#include "ns3/ipv4.h"
#include "ns3/log.h"
#include "ns3/ipv4-static-routing-helper.h"
#include "ns3/pointer.h"
#include "ns3/string.h"
#include "ns3/ipv4-routing-table-entry.h"

namespace ns3{


NS_LOG_COMPONENT_DEFINE ("ServeFrame");


TypeId
ServeFrame::GetTypeId(void)
{
    static TypeId tid = TypeId ("ns3::ServeFrame")
        .SetParent<Application> ()
        .AddConstructor<ServeFrame> ()
        .SetGroupName ("Internet-Apps")
        .AddAttribute ("CompeteTime", "Time for competing central node",
                        TimeValue (Seconds (20)),
                        MakeTimeAccessor (&ServeFrame::m_compete),
                        MakeTimeChecker ());

    return tid;   
}


ServeFrame::ServeFrame(){
    NS_LOG_FUNCTION (this);
    m_socket = 0;
    m_finishCompete = EventId();
    m_requCenterEvent = EventId();
    m_minAddress = Ipv4Address("10.0.0.2");
    m_maxAddress = Ipv4Address("10.0.0.32");


    m_firstBoot = true;
}


ServeFrame::ServeFrame(Ptr<NetDevice> netDevice){
    NS_LOG_FUNCTION (this);
    m_socket = 0;
    m_finishCompete = EventId();
    m_requCenterEvent = EventId();
    m_minAddress = Ipv4Address("10.0.0.2");
    m_maxAddress = Ipv4Address("10.0.0.32");

    m_device = netDevice;
    m_firstBoot = true;
}
ServeFrame::~ServeFrame(){
    NS_LOG_FUNCTION (this);
}


Ptr<NetDevice> ServeFrame::GetServeNetDevice(void)
{
    return m_device;
}

void
ServeFrame::SetServeNetDevice(Ptr<NetDevice> netDevice)
{
    m_device = netDevice;
}

void
ServeFrame::LinkStateHandler(void)
{
    NS_LOG_FUNCTION (this);

    // 网卡所连接的链路是通的 
    if(m_device->IsLinkUp ())
    {
        //std::cout<<"通通"<<std::endl;
        NS_LOG_INFO ("Link up at " << Simulator::Now ().As (Time::S));
        m_socket->SetRecvCallback (MakeCallback (&ServeFrame::NetHandler, this));
        StartApplication();
    }
    else
    {
        NS_LOG_INFO ("Link down at " << Simulator::Now ().As (Time::S)); //reinitialization
    }
}


void
ServeFrame::StartApplication (void)
{
    NS_LOG_FUNCTION (this);
    // NS_ASSERT_MSG (m_minAddress < m_maxAddress,"Invalid Address range");

    Ptr<Ipv4> ipv4 = GetNode ()->GetObject<Ipv4> ();
    uint32_t ifIndex = ipv4->GetInterfaceForDevice (m_device);

    // 这一段是获取自己的IP地址
    // 或者是获取MAC地址
    Address myAddress = m_device->GetAddress ();
    NS_LOG_INFO ("My address is " << myAddress);
    uint8_t addr[Address::MAX_SIZE];
    std::memset (addr, 0, Address::MAX_SIZE);
    uint32_t len = myAddress.CopyTo (addr);
    NS_ASSERT_MSG (len <= 16, "???? client can not handle a chaddr larger than 16 bytes");
    m_chaddr.CopyFrom (addr, 16);
    NS_LOG_INFO ("My m_chaddr is " << m_chaddr);

    // 这个found是舍么意思
    bool found = false;
    // 遍历协议中的所有
    // 这里绑定的是对方的IP？？
    for(uint32_t i = 0;i < ipv4->GetNAddresses(ifIndex);i++){
        if (ipv4->GetAddress (ifIndex, i).GetLocal () == m_myAddress)
        {
            found = true;
        }
    }
    if (!found)
    {
        ipv4->AddAddress (ifIndex, Ipv4InterfaceAddress (Ipv4Address ("10.0.0.0"),Ipv4Mask ("/24")));
    }
    // 没有socket就新建
    if(m_socket == 0)
    {
        TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
        m_socket = Socket::CreateSocket (GetNode (), tid);
        InetSocketAddress local = InetSocketAddress (Ipv4Address::GetAny (), SERVE_PORT);
        m_socket->SetAllowBroadcast (true);
        m_socket->BindToNetDevice (m_device);
        m_socket->Bind (local);
    }
    // 绑定socket接受到数据包的回调
    m_socket->SetRecvCallback(MakeCallback(&ServeFrame::NetHandler, this));
    // 这里最后再实现一个socket实现tag 后话

    // 首次启动应该
    if(m_firstBoot)
    {
        // std::cout<<"first boot"<<std::endl;
        m_state = COMPETING_CENTER;
        m_device->AddLinkChangeCallback(MakeCallback(&ServeFrame::LinkStateHandler, this));
        m_firstBoot = false;
    }

    Boot();
}
//启动直到完成中心节点的竞争
void ServeFrame::Boot(void)
{
    NS_LOG_FUNCTION (this);
    if(m_state == ServeFrame::COMPETING_CENTER)
    {
        //std::cout<<"Boot"<<std::endl;
        SendRequCenter();
    }
    // 200s时结束
    Simulator::Schedule(m_compete, &ServeFrame::StopCompete, this);
}

// 竞争结束进行的操作
void ServeFrame::StopCompete(void)
{
    //设为中心节点
    if(m_state == ServeFrame::COMPETING_CENTER)
    {
        
    }
    // 竞争失败
    if(m_state == ServeFrame::STOP_COMPETING)
    {

    }

    // 不再进行发送 自举中心的勤务帧
    m_requCenterEvent.Cancel();

}

// 发送自举中心勤务帧
void ServeFrame::SendRequCenter (void)
{
    NS_LOG_FUNCTION (this);

    // std::cout<<"发送自举中心勤务帧"<<std::endl;

    ServeHeader header;
    Ptr<Packet> packet;
    packet = Create<Packet> ();

    // 勤务帧类型为自举中心
    header.SetType(ServeHeader::REQUCENTER);
    // 设置ID 获取MAC地址的后16位
    // 这里是认为MAC地址的分配是根据节点的id来的，所以先用Id代替了
    // 返回的是MAC地址，但是类型是20字节的数组
    Address mac =  m_device->GetAddress();
    //std::cout<<"mac长度"<<mac<<std::endl;
    // 这里有个问题为么转换为为MAC48后都是00 00 00 00 00 00 00了
    //std::cout<<"mac长度"<<static_cast<int>(mac.GetLength())<<std::endl;
    //Mac48Address mac48;
    //mac48.ConvertFrom(mac);
    //std::cout<<"mac48"<<mac48<<std::endl;
    uint8_t macBuffer[6];
    mac.CopyTo (macBuffer);

    header.SetIdHigh(macBuffer[4]);
    header.SetIdLow (macBuffer[5]);
    //std::cout<<"节点的MAC值"<<macBuffer<<std::endl;
    //std::cout<<"节点的ID值"<<static_cast<int>(macBuffer[5])<<std::endl;

    // 添加到包的头部
    packet->AddHeader(header);
    
    if((m_socket->SendTo (packet, 0, InetSocketAddress (Ipv4Address ("255.255.255.255"), SERVE_PORT))) >= 0)
    {
        //std::cout<<"test"<<std::endl;
        NS_LOG_INFO ("Request for central node serve frame sent");
    }
    else
    {
        NS_LOG_INFO ("Error while sending request for central serve frame ");
    }
    // 此时状态为1 中心节点竞争中 
    m_state = COMPETING_CENTER;


    // 这里需要实现重传时间随机
    RngSeedManager::SetSeed(1);
    Ptr<UniformRandomVariable> uv = CreateObject<UniformRandomVariable>();

    uv->SetAttribute ("Min", DoubleValue (184));
    uv->SetAttribute ("Max", DoubleValue (216));
    m_rtrs = MilliSeconds (uv->GetInteger());
    
    m_requCenterEvent = Simulator::Schedule(m_rtrs, &ServeFrame::SendRequCenter, this);
}

// 收到自举中心勤务帧操作
void ServeFrame::RequCenterHander(ServeHeader header)
{
    //std::cout<<"收到执行"<<std::endl;
    uint8_t idHigh = header.GetSourIdHigh();
    uint8_t idLow = header.GetSourIdLow();

    // 获取本网卡的MAC(ID)
    Address mac =  m_device->GetAddress();
    //Mac48Address mac48;
    //mac48.ConvertFrom(mac);
    uint8_t macBuffer[6];
    mac.CopyTo (macBuffer);

    // 对方id比我大 继续自举中心
    if(idHigh > macBuffer[4]){
        return ;
    }
    // 对方id小
    else if(idHigh < macBuffer[4]){
        // 退出竞争 网络状态序号为 0 
        // 退出竞争后是不是需要发送同步帧
        m_state = STOP_COMPETING;
        //std::cout<< macBuffer[4]<<": 退出竞争 "<<std::endl;
        m_requCenterEvent.Cancel();
    }
    else{
        //对方id比我大
        if(idLow > macBuffer[5]){
            return ;
        }
        else{
            m_state = STOP_COMPETING;
            std::cout<< macBuffer[4]<<": 退出竞争 "<<std::endl;
            m_requCenterEvent.Cancel();
        }
    }
}


// 处理接收帧的函数
void ServeFrame::NetHandler(Ptr<Socket> socket)
{
    NS_LOG_FUNCTION (this << socket);

    //std::cout<<"收到"<<std::endl;
    ServeHeader header;
    Ptr<Packet> packet = 0;
    Address from;
    packet = m_socket->RecvFrom(from);

    // 获得发送方的地址并转换，勤务帧是不是需要广播？先设置了后面用不用再说
    //InetSocketAddress senderAddr = InetSocketAddress::ConvertFrom(from);

    // 获取数据包中的Tag 这里的Tag不可能是对方发过来的，只能是下层(链路层)的
    Ipv4PacketInfoTag interfaceInfo;
    //if (!packet->RemovePacketTag(interfaceInfo))
    //{
    //    NS_ABORT_MSG("No incoming interface on Serve Frame message, aborting.");
    //}
    // 猜想是获取接口的序号
    uint32_t incomingIf = interfaceInfo.GetRecvIf ();
    Ptr<NetDevice> iDev = GetNode ()->GetDevice (incomingIf);

    if(packet->RemoveHeader (header) == 0){
        return ;
    }
    // 收到中心节点竞争勤务帧
    // 2023/03/28 现在只能收到自举中心勤务帧
    if(m_state == COMPETING_CENTER && header.GetType () == ServeHeader::REQUCENTER)
    {
        // std::cout<<
        RequCenterHander(header);
    }
}


void
ServeFrame::StopApplication()
{
    NS_LOG_FUNCTION (this);

    m_requCenterEvent.Cancel();
    m_finishCompete.Cancel();

    Ptr<Ipv4> ipv4 = GetNode ()->GetObject<Ipv4> ();

    int32_t ifIndex = ipv4->GetInterfaceForDevice (m_device);
    for (uint32_t i = 0; i < ipv4->GetNAddresses (ifIndex); i++)
    {
        if (ipv4->GetAddress (ifIndex,i).GetLocal () == m_myAddress)
        {
            ipv4->RemoveAddress (ifIndex, i);
            break;
        }
    }

    m_socket->SetRecvCallback (MakeNullCallback<void, Ptr<Socket> > ());
    m_socket->Close ();
}


}//namespace ns3